package com.aner.tester.resolver;

import com.aner.tester.constant.Constant;
import com.aner.tester.object.ControllerObject;
import com.aner.tester.object.MethodObject;
import com.aner.tester.utils.text.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import com.aner.tester.utils.LoggerUtils;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * the resolver of controller
 *
 * @author aner
 * @version 1.0
 * @date 2020/12/14 23:47
 */
public class ControllerResolver {

    private static Logger logger = LoggerUtils.getLogger(ControllerResolver.class);

    private static Set<Class<? extends Annotation>> mappingAnnotationSet = new HashSet<>();

    static {
        mappingAnnotationSet.add(PostMapping.class);
        mappingAnnotationSet.add(GetMapping.class);
        mappingAnnotationSet.add(PutMapping.class);
        mappingAnnotationSet.add(DeleteMapping.class);
        mappingAnnotationSet.add(PatchMapping.class);
        mappingAnnotationSet.add(RequestMapping.class);

    }
    public  static List<ControllerObject> resolve(Set<Class<?>> entitySet) {
        List<ControllerObject> controllerObjectList = new ArrayList<>();
        if (entitySet != null) {
            // iterate all controller
            for (Class<?> entity : entitySet) {
                String classMappingValue = getControllerMappingValue(entity);
                ControllerObject controllerObject = new ControllerObject();
                controllerObject.setClassName(entity.getSimpleName());
                controllerObject.setPackageName(getPackage(entity.getName()));

                List<MethodObject> genObjectMethodList = new ArrayList<>();
                // filter mapping api methods
                List<Method> methods = Arrays.stream(entity.getDeclaredMethods())
                        .filter(method -> mappingMethodExisted(method)).collect(Collectors.toList());
                // resolve all method of a controller
                for (Method method : methods) {
                    MethodObject methodObject = MethodResolver.resolve(controllerObject, method, classMappingValue);
                    genObjectMethodList.add(methodObject);
                }

                controllerObject.setMethods(genObjectMethodList);
                controllerObjectList.add(controllerObject);
            }
        }
        return controllerObjectList;
    }

    public static boolean mappingMethodExisted(Method method) {
        return mappingAnnotationSet.stream().filter(cls -> method.isAnnotationPresent(cls)).count() > 0;
    }

    /**
     * get the mapping value of controller
     *
     * @param clz
     * @return
     */
    public  static String getControllerMappingValue(Class clz){
        if (clz.isAnnotationPresent(RequestMapping.class)) {
            RequestMapping mapping = (RequestMapping) clz.getAnnotation(RequestMapping.class);
            String[] values = mapping.value();
            if (values != null && values.length > 0) {
                return values[0];
            }
        }
        return Constant.PATH_SLASH;
    }

    /**
     * get package name of class
     *
     * @param className
     * @return
     */
    public static String getPackage(String className) {
        if (StringUtils.isNotEmpty(className)) {
            return className.substring(0, className.lastIndexOf('.'));
        } else {
            return Strings.EMPTY;
        }
    }
}
